package org.appfuse.tutorial.webapp.action;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationManager;
import com.opensymphony.xwork2.config.providers.XWorkConfigurationProvider;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.util.LocalizedTextUtil;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.ValueStackFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.ServletActionContext;
import org.appfuse.Constants;
import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.subethamail.wiser.Wiser;

import javax.transaction.Transactional;
import java.net.BindException;
import java.util.HashMap;
import java.util.Random;

/**
 * Base class for running Struts 2 Action tests.
 *
 * @author <a href="mailto:matt@raibledesigns.com">Matt Raible</a>
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
		"classpath:/applicationContext-resources.xml",
		"classpath:/applicationContext-dao.xml",
		"classpath:/applicationContext-service.xml",
		"classpath*:/applicationContext.xml",
		"classpath:**/applicationContext*.xml"
})
@Transactional
public abstract class BaseActionTestCase {
	protected transient final Log log = LogFactory.getLog(getClass());
	private int smtpPort;

	@Autowired
	protected JavaMailSenderImpl mailSender;

	@Before
	public void onSetUp() {
		smtpPort = (new Random().nextInt(9999 - 1000) + 1000);
		log.debug("SMTP Port set to: " + smtpPort);

		LocalizedTextUtil.addDefaultResourceBundle(Constants.BUNDLE_KEY);

		// Initialize ActionContext
		ConfigurationManager configurationManager = new ConfigurationManager();
		configurationManager.addContainerProvider(new XWorkConfigurationProvider());
		Configuration config = configurationManager.getConfiguration();
		Container container = config.getContainer();

		ValueStack stack = container.getInstance(ValueStackFactory.class).createValueStack();
		stack.getContext().put(ActionContext.CONTAINER, container);
		ActionContext.setContext(new ActionContext(stack.getContext()));

		ActionContext.getContext().setSession(new HashMap<String, Object>());

		// populate the request so getRequest().getSession() doesn't fail in BaseAction.java
		ServletActionContext.setRequest(new MockHttpServletRequest());
	}

	protected int getSmtpPort() {
		return smtpPort;
	}

	protected Wiser startWiser(int smtpPort) {
		Wiser wiser = new Wiser();
		wiser.setPort(smtpPort);
		try {
			wiser.start();
		} catch (RuntimeException re) {
			if (re.getCause() instanceof BindException) {
				int nextPort = smtpPort + 1;
				if (nextPort - smtpPort > 10) {
					log.error("Exceeded 10 attempts to start SMTP server, aborting...");
					throw re;
				}
				log.error("SMTP port " + smtpPort + " already in use, trying " + nextPort);
				return startWiser(nextPort);
			}
		}
		mailSender.setPort(smtpPort);
		return wiser;
	}

	@After
	public void onTearDown() {
		ActionContext.getContext().setSession(null);
	}
}
